/**************************************************************************************
*
*	Copyright (C) 2010 - 2012 Brent Andrew O. Chua - See LICENSE.txt for more info.
*		
*	Filename	-	DepthStencilState.cpp
*
*	Comments	-	See DepthStencilState.h
*
**************************************************************************************/
#include "../Include/DepthStencilState.h"
#include "../Include/Engine.h"
#include "../Include/GraphicsDevice.h"
#include "../Include/AsyncLoadResource.h"
#include "../Include/XMLParser.h"
#include "../Include/GraphicsTypesMapping.h"

namespace Pulse
{
	DepthStencilStateDesc::DepthStencilStateDesc( void )
	{
		SetDefault();
	}

	void DepthStencilStateDesc::SetToLoad( const CHAR *pFilename )
	{
		m_filename = pFilename;
	}

	void DepthStencilStateDesc::SetDefault( void )
	{
		bDepthEnable		= TRUE;
		depthWriteMask		= EDepthWriteMask::ALL;
		depthFunc			= EComparisonFunc::LESS;
		bStencilEnable		= FALSE;
		stencilReadMask		= EStencilReadMask::DEFAULT;
		stencilWriteMask	= EStencilWriteMask::DEFAULT;
		
		frontFace.failOp		= EStencilOp::KEEP;
		frontFace.depthFailOp	= EStencilOp::KEEP;
		frontFace.passOp		= EStencilOp::KEEP;
		frontFace.func			= EComparisonFunc::ALWAYS;

		backFace.failOp			= EStencilOp::KEEP;
		backFace.depthFailOp	= EStencilOp::KEEP;
		backFace.passOp			= EStencilOp::KEEP;
		backFace.func			= EComparisonFunc::ALWAYS;
	}

	DepthStencilState::DepthStencilState( void )
		: m_pDepthStencilState( PSX_NULL )
	{
	
	}

	DepthStencilState::~DepthStencilState( void )
	{

	}

	BOOL DepthStencilState::CreateResource( BaseResDesc *pDesc )	
	{
		if ( pDesc->GetType() != GetType() )
			return FALSE;

		m_desc = *((DepthStencilStateDesc*)pDesc);

		if ( PerformInitialize( m_desc.IsAsyncLoad() ) == EErrorCode::OKAY )
			SetFlagCreated();

		return IsCreated();	
	}

	BOOL DepthStencilState::DestroyResource( void )				
	{
		UnloadResource();
		SetFlagDestroyed();	
		return TRUE;	
	}

	BOOL DepthStencilState::LoadResource( void )				
	{ 
		if ( Initialize() == EErrorCode::OKAY )
		{
			SetFlagLoaded();	
			return TRUE;	
		}

		return FALSE;
	}

	BOOL DepthStencilState::UnloadResource( void )				
	{
		Cleanup();
		SetFlagUnloaded();
		return FALSE;	
	}

	EErrorCode::Type DepthStencilState::Initialize( void )
	{
		ID3D11Device *pDevice = (ID3D11Device*)GraphicsDevice::GetInstance()->GetNakedDevice();

		if ( m_desc.m_filename.IsEmpty() == FALSE )
		{
			XMLDocumentPtr pDoc = XMLParser::Load( (PSX_PATH_DEPTH_STENCIL_STATES + m_desc.m_filename).GetBuffer() );

			if ( pDoc.IsNull() )
			{
				PSX_PushError( "Failed to load depth stencil state file." );
				return EErrorCode::FILE;
			}

			XMLElementPtr pElem = pDoc->FindChild( PSX_String("DepthStencilState") );

			if ( pElem.IsValid() )
			{
				EErrorCode::Type retCode = ProcessDepthStencilState( pElem );

				if (  retCode != EErrorCode::OKAY )
				{
					return retCode;
				}
			}
			else
			{
				return EErrorCode::INVALID_FORMAT;
			}
		}

		D3D11_DEPTH_STENCIL_DESC desc;

		desc.DepthEnable		= m_desc.bDepthEnable;
		desc.DepthWriteMask		= DX11Mapping::ToDXDepthWriteMask( m_desc.depthWriteMask );
		desc.DepthFunc			= DX11Mapping::ToDXComparisonFunc( m_desc.depthFunc );
		desc.StencilEnable		= m_desc.bStencilEnable;
		desc.StencilReadMask	= DX11Mapping::ToDXStencilReadMask( m_desc.stencilReadMask );
		desc.StencilWriteMask	= DX11Mapping::ToDXStencilWriteMask( m_desc.stencilWriteMask );
		
		desc.FrontFace.StencilFailOp		= DX11Mapping::ToDXStencilOp( m_desc.frontFace.failOp );
		desc.FrontFace.StencilDepthFailOp	= DX11Mapping::ToDXStencilOp( m_desc.frontFace.depthFailOp );
		desc.FrontFace.StencilPassOp		= DX11Mapping::ToDXStencilOp( m_desc.frontFace.passOp );
		desc.FrontFace.StencilFunc			= DX11Mapping::ToDXComparisonFunc( m_desc.frontFace.func );

		desc.BackFace.StencilFailOp			= DX11Mapping::ToDXStencilOp( m_desc.backFace.failOp );
		desc.BackFace.StencilDepthFailOp	= DX11Mapping::ToDXStencilOp( m_desc.backFace.depthFailOp );
		desc.BackFace.StencilPassOp			= DX11Mapping::ToDXStencilOp( m_desc.backFace.passOp );
		desc.BackFace.StencilFunc			= DX11Mapping::ToDXComparisonFunc( m_desc.backFace.func );

		if ( pDevice->CreateDepthStencilState( &desc, &m_pDepthStencilState ) != S_OK )
		{
			PSX_PushError( "Failed to create depth stencil state." );
			return EErrorCode::GRAPHICS;
		}

		return EErrorCode::NONE;
	}

	void DepthStencilState::Cleanup( void )
	{
		PSX_SafeRelease( m_pDepthStencilState );
	}

	EErrorCode::Type DepthStencilState::ProcessDepthStencilState( XMLElementPtr pElem )
	{
		XMLElementPtr pChild; 

		// DepthEnable
		{
			pChild = pElem->FindChild( PSX_String("DepthEnable") );

			if ( pChild.IsNull() )
			{
				PSX_PushError( "No DepthEnable found." );
				return EErrorCode::INVALID_FORMAT;
			}

			m_desc.bDepthEnable = pChild->GetValue().ToBOOL();
		}

		// DepthWriteMask
		{
			pChild = pElem->FindChild( PSX_String("DepthWriteMask") );

			if ( pChild.IsNull() )
			{
				PSX_PushError( "No DepthWriteMask found." );
				return EErrorCode::INVALID_FORMAT;
			}

			m_desc.depthWriteMask = GraphicsTypesMapping::ToDepthWriteMask( pChild->GetValue().GetBuffer() );
		}

		// DepthFunc
		{
			pChild = pElem->FindChild( PSX_String("DepthFunc") );

			if ( pChild.IsNull() )
			{
				PSX_PushError( "No DepthFunc found." );
				return EErrorCode::INVALID_FORMAT;
			}

			m_desc.depthFunc = GraphicsTypesMapping::ToComparisonFunc( pChild->GetValue().GetBuffer() );
		}

		// StencilEnable
		{
			pChild = pElem->FindChild( PSX_String("StencilEnable") );

			if ( pChild.IsNull() )
			{
				PSX_PushError( "No StencilEnable found." );
				return EErrorCode::INVALID_FORMAT;
			}

			m_desc.bStencilEnable = pChild->GetValue().ToBOOL();
		}

		// StencilReadMask
		{
			pChild = pElem->FindChild( PSX_String("StencilReadMask") );

			if ( pChild.IsNull() )
			{
				PSX_PushError( "No StencilReadMask found." );
				return EErrorCode::INVALID_FORMAT;
			}

			m_desc.stencilReadMask = GraphicsTypesMapping::ToStencilReadMask( pChild->GetValue().GetBuffer() );
		}

		// StencilWriteMask
		{
			pChild = pElem->FindChild( PSX_String("StencilWriteMask") );

			if ( pChild.IsNull() )
			{
				PSX_PushError( "No StencilWriteMask found." );
				return EErrorCode::INVALID_FORMAT;
			}

			m_desc.stencilWriteMask = GraphicsTypesMapping::ToStencilWriteMask( pChild->GetValue().GetBuffer() );
		}

		// FrontFace
		{
			pChild = pElem->FindChild( PSX_String("FrontFace") );

			if ( pChild.IsNull() )
			{
				PSX_PushError( "No FrontFace found." );
				return EErrorCode::INVALID_FORMAT;
			}

			EErrorCode::Type retCode = ProcessStencilOp( pChild, &m_desc.frontFace );
		
			if ( retCode != EErrorCode::OKAY)
			{
				return retCode;
			}
		}

		// BackFace
		{
			pChild = pElem->FindChild( PSX_String("BackFace") );

			if ( pChild.IsNull() )
			{
				PSX_PushError( "No BackFace found." );
				return EErrorCode::INVALID_FORMAT;
			}

			EErrorCode::Type retCode = ProcessStencilOp( pChild, &m_desc.backFace );
		
			if ( retCode != EErrorCode::OKAY)
			{
				return retCode;
			}
		}

		return EErrorCode::OKAY;
	}

	EErrorCode::Type DepthStencilState::ProcessStencilOp( XMLElementPtr pElem, StencilOp *pOp )
	{
		XMLElementPtr pChild; 

		// FailOp
		{
			pChild = pElem->FindChild( PSX_String("FailOp") );

			if ( pChild.IsNull() )
			{
				PSX_PushError( "No FailOp found." );
				return EErrorCode::INVALID_FORMAT;
			}

			pOp->failOp = GraphicsTypesMapping::ToStencilOp( pChild->GetValue().GetBuffer() );
		}

		// DepthFailOp
		{
			pChild = pElem->FindChild( PSX_String("DepthFailOp") );

			if ( pChild.IsNull() )
			{
				PSX_PushError( "No DepthFailOp found." );
				return EErrorCode::INVALID_FORMAT;
			}

			pOp->depthFailOp = GraphicsTypesMapping::ToStencilOp( pChild->GetValue().GetBuffer() );
		}

		// PassOp
		{
			pChild = pElem->FindChild( PSX_String("PassOp") );

			if ( pChild.IsNull() )
			{
				PSX_PushError( "No PassOp found." );
				return EErrorCode::INVALID_FORMAT;
			}

			pOp->passOp = GraphicsTypesMapping::ToStencilOp( pChild->GetValue().GetBuffer() );
		}

		// Func
		{
			pChild = pElem->FindChild( PSX_String("Func") );

			if ( pChild.IsNull() )
			{
				PSX_PushError( "No Func found." );
				return EErrorCode::INVALID_FORMAT;
			}

			pOp->func = GraphicsTypesMapping::ToComparisonFunc( pChild->GetValue().GetBuffer() );
		}

		return EErrorCode::OKAY;
	}

}